//
//  Copyright (C) 2011 Ray Wang(fangshi168775@hotmail.com).  All Rights Reserved.
//
//  This is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This software is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this software; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
//  USA.
//

package ray.pcvnccontroler;

import java.io.IOException;
import android.view.KeyEvent;
import java.lang.Runnable;
import android.os.Handler;

public class KeySender {

	RfbProto rfb;
	public static final int START_SHORTCUT = 1;
	public static final int DESKTOP_SHORTCUT = 2;
	public static final int CANCEL_SHORTCUT = 3;
	public static final int CLOSE_PAGE_SHORTCUT = 4;
	public static final int CLOSE_WINDOW_SHORTCUT = 5;
	public static final int IE_SHORTCUT = 6;
	public static final int FOCUS_CHANGE_SHROTCUT = 7;
	public static final int RIGHT_KEY_SHORTCUT = 8;
	public static final int PROGRAM_MENU_SHORTCUT = 11;
	public static final int MY_COMPUTER_SHORTCUT = 12;
	public static final int SEARCH_SHORTCUT = 13;
	public static final int ZOOM_SHORTCUT = 14;
	public static final int WINDOW_CHANGE_SHORTCUT = 15;
	public static final int LOCK_WINDOW_SHORTCUT = 16;
	public static final int SHUTDOWN_SHORTCUT = 17;
	public static final int MUTE_SHORTCUT = 18;
	public static final int RUN_SHORTCUT = 19;
	public static final int VOL_UP_START_SHORTCUT = 20;
	public static final int VOL_UP_END_SHORTCUT = 21;
	public static final int VOL_DOWN_START_SHORTCUT = 22;
	public static final int VOL_DOWN_END_SHORTCUT = 23;
	public static final int BACK_SHORTCUT = 24;
	public static final int FORWARD_SHORTCUT  = 25;
	public static final int PASTE_SHORTCUT = 26;
	public static final int DELETE_SHORTCUT = 27;
	
	private static final int VOL_NULL_CTRL	= 0;
	private static final int VOL_MUTE_0_CTRL = VOL_NULL_CTRL + 1;
	private static final int VOL_MUTE_1_CTRL = VOL_MUTE_0_CTRL + 1;
	private static final int VOL_UP_0_CTRL = VOL_MUTE_1_CTRL + 1;
	private static final int VOL_UP_1_CTRL = VOL_UP_0_CTRL + 1;
	private static final int VOL_DOWN_0_CTRL = VOL_UP_1_CTRL + 1;
	private static final int VOL_DOWN_1_CTRL = VOL_DOWN_0_CTRL + 1;
	private static final int VOL_CLOSE_CTRL = VOL_DOWN_1_CTRL + 1;
	
	private static final int VOL_CTRL_DELAY = 500;
	
	private static final int BACKSPACE_KEY = 0xff08;
	private static final int TAB_KEY = 0xff09;
	private static final int ENTER_KEY = 0xff0d;
	private static final int PAUSE_KEY = 0xff13;
	private static final int SYS_REQ_KEY = 0xff15;
	private static final int ESCAPE_KEY = 0xff1b;
	private static final int DELETE_KEY = 0xffff;
	
	private static final int HOME_KEY = 0xff50;
	private static final int LEFT_KEY = 0xff51;
	private static final int UP_KEY = 0xff52;
	private static final int RIGHT_KEY = 0xff53;
	private static final int DOWN_KEY = 0xff54;
	private static final int PAGE_UP_KEY = 0xff55;
	private static final int PAGE_DOWN_KEY = 0xff56;
	private static final int END_KEY = 0xff57;
	
	private static final int PRINT_KEY = 0xff61;
	private static final int INSERT_KEY = 0xff63;
	private static final int NUM_LOCK_KEY = 0xff7f;
	
	private static final int SHIFT_LEFT_KEY = 0xffe1;
	private static final int SHIFT_RIGHT_KEY = 0xffe2;
	private static final int CTRL_LEFT_KEY = 0xffe3;
	private static final int CTRL_RIGHT_KEY = 0xffe4;
	private static final int CAPS_LOCK_KEY = 0xffe5;
	private static final int SUPER_LEFT_KEY = 0xffeb;
	private static final int SUPER_RIGHT_KEY = 0xffec;
	
	private static final int F1_KEY = 0xffbe;
	private static final int F2_KEY = 0xffbf;
	private static final int F3_KEY = 0xffc0;
	private static final int F4_KEY = 0xffc1;
	private static final int F5_KEY = 0xffc2;
	private static final int F6_KEY = 0xffc3;
	private static final int F7_KEY = 0xffc4;
	private static final int F8_KEY = 0xffc5;
	private static final int F9_KEY = 0xffc6;
	private static final int F10_KEY = 0xffc7;
	private static final int F11_KEY = 0xffc8;
	private static final int F12_KEY = 0xffc9;
	private static final int SPACE_KEY = 0x0020;
	private Handler handler;
	private VolRunnable volCallback;

	KeySender(RfbProto rfb)
	{
		this.rfb = rfb;
		handler = new Handler();
		volCallback = new VolRunnable();
	}
	
	public boolean SendKeyEvent(int keyCode, boolean down) throws IOException
	{
		int keySym;
		switch(keyCode)
		{
		case KeyEvent.KEYCODE_DPAD_LEFT:
			keySym = LEFT_KEY;
			break;
		case KeyEvent.KEYCODE_DPAD_RIGHT:
			keySym = RIGHT_KEY;
			break;
		case KeyEvent.KEYCODE_DPAD_UP:
			keySym = UP_KEY;
			break;
		case KeyEvent.KEYCODE_DPAD_DOWN:
			keySym = DOWN_KEY;
			break;
		case KeyEvent.KEYCODE_DPAD_CENTER:
			keySym = ENTER_KEY;
			break;
		default:
			return false;
		}
		rfb.writeKeyEvent(keySym, 0, down);
		return true;
	}
	
	public void SendShortcut(int shortcut) throws IOException
	{
		switch(shortcut)
		{
		case START_SHORTCUT:
			SendStartShortcut();
			break;
		case DESKTOP_SHORTCUT:
			SendDesktopShortcut();
			break;
		case CANCEL_SHORTCUT:
			SendCancelShortcut();
			break;
		case CLOSE_PAGE_SHORTCUT:
			SendClosePageShortcut();
			break;
		case CLOSE_WINDOW_SHORTCUT:
			SendCloseWindowShortcut();
			break;
		case IE_SHORTCUT:
			SendIEShortcut();
			break;
		case FOCUS_CHANGE_SHROTCUT:						
			SendFocusChangeShortcut();
			break;
		case RIGHT_KEY_SHORTCUT:
			SendRightKeyShortcut();
			break;
		case PROGRAM_MENU_SHORTCUT:
			SendMenuShortcut();
			break;
		case MY_COMPUTER_SHORTCUT:
			SendMyComputerShortcut();
			break;
		case SEARCH_SHORTCUT:
			SendSearchShortcut();
			break;
		case ZOOM_SHORTCUT:
			SendZoomShortcut();
			break;
		case WINDOW_CHANGE_SHORTCUT:
			SendWindowChangeShortcut();
			break;
		case LOCK_WINDOW_SHORTCUT:
			SendLockWindowShortcut();
			break;
		case SHUTDOWN_SHORTCUT:
			SendShutdownShortcut();
			break;
		case MUTE_SHORTCUT:
			SendMuteShortcut();
			break;
		case RUN_SHORTCUT:
			SendRunShortcut();
			break;
		case VOL_UP_START_SHORTCUT:
			SendVolUpStartShortcut();
			break;
		case VOL_UP_END_SHORTCUT:
			SendVolUpEndShortcut();
			break;
		case VOL_DOWN_START_SHORTCUT:
			SendVolDownStartShortcut();
			break;
		case VOL_DOWN_END_SHORTCUT:
			SendVolDownEndShortcut();
			break;
		case BACK_SHORTCUT:
			SendBackShortcut();
			break;
		case FORWARD_SHORTCUT:
			SendForwardShortcut();
			break;
		case PASTE_SHORTCUT:
			SendPasteShortcut();
			break;
		case DELETE_SHORTCUT:
			SendDeleteShortcut();
			break;
		}
	}
	
	private void SendDeleteShortcut() throws IOException
	{
		rfb.writeKeyEvent(BACKSPACE_KEY, 0, true);
		rfb.writeKeyEvent(BACKSPACE_KEY, 0, false);
	}
	
	private void SendPasteShortcut() throws IOException
	{
		rfb.writeKeyEvent(CTRL_LEFT_KEY,0 , true);
		rfb.writeKeyEvent('v', 0, true);
		rfb.writeKeyEvent('v', 0, false);
		rfb.writeKeyEvent(CTRL_LEFT_KEY, 0, false);
	}
	
	private void SendBackShortcut() throws IOException
	{
		rfb.writeKeyEvent(LEFT_KEY, VncMask.ALT_MASK, true);
		rfb.writeKeyEvent(LEFT_KEY, VncMask.ALT_MASK, false);
	}
	
	private void SendForwardShortcut() throws IOException
	{
		rfb.writeKeyEvent(RIGHT_KEY, VncMask.ALT_MASK, true);
		rfb.writeKeyEvent(RIGHT_KEY, VncMask.ALT_MASK, false);
	}
	
	private void SendStartShortcut() throws IOException
	{
		rfb.writeKeyEvent(SUPER_LEFT_KEY, 0, true);
		rfb.writeKeyEvent(SUPER_LEFT_KEY, 0, false);
	}
	
	private void SendDesktopShortcut() throws IOException
	{
		rfb.writeKeyEvent('d', VncMask.SUPER_MASK, true);
		rfb.writeKeyEvent('d', VncMask.SUPER_MASK, false);
	}
	
	private void SendCancelShortcut() throws IOException
	{
		rfb.writeKeyEvent(ESCAPE_KEY, 0, true);
		rfb.writeKeyEvent(ESCAPE_KEY, 0, false);
	}
	
	private void SendClosePageShortcut() throws IOException
	{
		rfb.writeKeyEvent(F4_KEY, VncMask.CTRL_MASK, true);
		rfb.writeKeyEvent(F4_KEY, VncMask.CTRL_MASK, false);
	}
	
	private void SendCloseWindowShortcut() throws IOException
	{
		rfb.writeKeyEvent(F4_KEY, VncMask.ALT_MASK, true);
		rfb.writeKeyEvent(F4_KEY, VncMask.ALT_MASK, false);
	}
	
	private void SendMenuShortcut() throws IOException
	{
		rfb.writeKeyEvent(F10_KEY, 0, true);
		rfb.writeKeyEvent(F10_KEY, 0, false);
	}
	
	private void SendFocusChangeShortcut() throws IOException
	{
		rfb.writeKeyEvent(TAB_KEY, 0, true);
		rfb.writeKeyEvent(TAB_KEY, 0, false);
	}
	
	private void SendMyComputerShortcut() throws IOException
	{
		rfb.writeKeyEvent('e', VncMask.SUPER_MASK, true);
		rfb.writeKeyEvent('e', VncMask.SUPER_MASK, false);
	}
	
	private void SendSearchShortcut() throws IOException
	{
		rfb.writeKeyEvent('f', VncMask.SUPER_MASK, true);
		rfb.writeKeyEvent('f', VncMask.SUPER_MASK, false);
	}
	
	private void SendRunShortcut() throws IOException
	{
		rfb.writeKeyEvent('r', VncMask.SUPER_MASK, true);
		rfb.writeKeyEvent('r', VncMask.SUPER_MASK, false);
	}
	
	private void SendIEShortcut() throws IOException
	{
		String cmd = "iexplore.exe http://www.google.com.hk";
		int len = cmd.length();
		
		SendRunShortcut();
		for(int i=0; i<len; i++)
		{
			char c = cmd.charAt(i);
			rfb.writeKeyEvent(c, 0, true);
			rfb.writeKeyEvent(c, 0, false);
		}
		rfb.writeKeyEvent(ENTER_KEY, 0, true);
		rfb.writeKeyEvent(ENTER_KEY, 0, false);
	}
	
	private void SendRightKeyShortcut() throws IOException
	{
		rfb.writeKeyEvent(F10_KEY, VncMask.SHIFT_MASK, true);
		rfb.writeKeyEvent(F10_KEY, VncMask.SHIFT_MASK, false);
	}
	
	private void SendZoomShortcut() throws IOException
	{
		rfb.writeKeyEvent(SPACE_KEY, VncMask.ALT_MASK, true);
		rfb.writeKeyEvent(SPACE_KEY, VncMask.ALT_MASK, false);
	}
	
	private void SendWindowChangeShortcut() throws IOException
	{
		rfb.writeKeyEvent(TAB_KEY, VncMask.SUPER_MASK, true);
		rfb.writeKeyEvent(TAB_KEY, VncMask.SUPER_MASK, false);	
	}
	
	private void SendLockWindowShortcut() throws IOException
	{
		rfb.writeKeyEvent('l', VncMask.SUPER_MASK, true);
		rfb.writeKeyEvent('l', VncMask.SUPER_MASK, false);
	}
	
	private void SendShutdownShortcut() throws IOException
	{
		String cmd = "shutdown -f -s -t 0";
		int len = cmd.length();
		
		SendRunShortcut();
		for(int i=0; i<len; i++)
		{
			char c = cmd.charAt(i);
			rfb.writeKeyEvent(c, 0, true);
			rfb.writeKeyEvent(c, 0, false);
		}
		rfb.writeKeyEvent(ENTER_KEY, 0, true);
		rfb.writeKeyEvent(ENTER_KEY, 0, false);
	}
	
	private void StartSndvol32 () throws IOException
	{
		String cmd = "sndvol32 -t";
		int len = cmd.length();
		
		SendRunShortcut();
		for(int i=0; i<len; i++)
		{
			char c = cmd.charAt(i);
			rfb.writeKeyEvent(c, 0, true);
			rfb.writeKeyEvent(c, 0, false);
		}
		rfb.writeKeyEvent(ENTER_KEY, 0, true);
		rfb.writeKeyEvent(ENTER_KEY, 0, false);
	}
	
	private void SendMuteShortcut() throws IOException
	{
		if(volCallback.GetCtlType() != VOL_NULL_CTRL)
			return;
		
		StartSndvol32();
		volCallback.SetCtlType(VOL_MUTE_0_CTRL);
		handler.postDelayed(volCallback, VOL_CTRL_DELAY);
	}
	
	private void SendVolUpStartShortcut() throws IOException
	{
		if(volCallback.GetCtlType() == VOL_NULL_CTRL)
		{
			StartSndvol32();
			volCallback.SetCtlType(VOL_UP_0_CTRL);
			handler.postDelayed(volCallback, VOL_CTRL_DELAY);
		}
		else if(volCallback.GetCtlType() == VOL_UP_1_CTRL)
		{
			rfb.writeKeyEvent(UP_KEY, 0, true);
		}
	}
	
	private void SendVolUpEndShortcut() throws IOException
	{
		if(volCallback.GetCtlType() != VOL_UP_0_CTRL)
		{
			handler.removeCallbacks(volCallback);
			volCallback.SetCtlType(VOL_NULL_CTRL);
		}
		else if(volCallback.GetCtlType() == VOL_UP_1_CTRL)
		{
			rfb.writeKeyEvent(UP_KEY, 0, false);
			volCallback.SetCtlType(VOL_CLOSE_CTRL);
			handler.postDelayed(volCallback, VOL_CTRL_DELAY);
		}
	}
	
	private void SendVolDownStartShortcut() throws IOException
	{
		if(volCallback.GetCtlType() == VOL_NULL_CTRL)
		{
			StartSndvol32();
			volCallback.SetCtlType(VOL_DOWN_0_CTRL);
			handler.postDelayed(volCallback, VOL_CTRL_DELAY);
		}
		else if(volCallback.GetCtlType() == VOL_DOWN_1_CTRL)
		{
			rfb.writeKeyEvent(DOWN_KEY, 0, true);
		}
	}
	
	private void SendVolDownEndShortcut() throws IOException
	{
		if(volCallback.GetCtlType() != VOL_DOWN_0_CTRL)
		{
			handler.removeCallbacks(volCallback);
			volCallback.SetCtlType(VOL_NULL_CTRL);
		}
		else if(volCallback.GetCtlType() == VOL_DOWN_1_CTRL)
		{
			rfb.writeKeyEvent(DOWN_KEY, 0, false);
			volCallback.SetCtlType(VOL_CLOSE_CTRL);
			handler.postDelayed(volCallback, VOL_CTRL_DELAY);
		}
	}
	
	public class VolRunnable implements Runnable 
	{
		private int ctltype;
		public VolRunnable()
		{
			this.ctltype = VOL_NULL_CTRL;
		}
		
		public void SetCtlType(int ctltype)
		{
			this.ctltype = ctltype;
		}
		
		public int GetCtlType()
		{
			return ctltype;
		}
		@Override
		public void run() 
		{
			switch(ctltype)
			{
			case VOL_MUTE_0_CTRL: //mute
				try {
					rfb.writeKeyEvent(TAB_KEY, 0, true);
					rfb.writeKeyEvent(TAB_KEY, 0, false);
					ctltype =  VOL_MUTE_1_CTRL;
					handler.postDelayed(volCallback, VOL_CTRL_DELAY);
					
				} catch (IOException e) {
					e.printStackTrace();
				}
				break;
			case VOL_MUTE_1_CTRL:
				try
				{
					rfb.writeKeyEvent(SPACE_KEY, 0, true);
					rfb.writeKeyEvent(SPACE_KEY, 0, false);
					ctltype =  VOL_CLOSE_CTRL;
					handler.postDelayed(volCallback, VOL_CTRL_DELAY);														
				}catch (IOException e) {
					e.printStackTrace();
				}
				break;
			case VOL_CLOSE_CTRL:
				try
				{
					SendCloseWindowShortcut();
					handler.removeCallbacks(volCallback);	
					ctltype = VOL_NULL_CTRL;
				}catch (IOException e) {
					
					e.printStackTrace();
				}
				break;
			case VOL_UP_0_CTRL: //vol up
				try {
					rfb.writeKeyEvent(UP_KEY, 0, true);
					ctltype = VOL_UP_1_CTRL;
					handler.removeCallbacks(volCallback);	
				} catch (IOException e) {
					
					e.printStackTrace();
				}
				break;
			case VOL_DOWN_0_CTRL: //vol down
				try {
					rfb.writeKeyEvent(DOWN_KEY, 0, true);
					ctltype = VOL_DOWN_1_CTRL;
					handler.removeCallbacks(volCallback);	
				} catch (IOException e) {
					
					e.printStackTrace();
				}
				break;	
			}
		}
		
	}
}
